////////////////////////////////////////////////////////////////////////////////

#ifndef _SERVERNETWORK_H_
#include "servernetwork.h"
#endif

////////////////////////////////////////////////////////////////////////////////

#ifndef _NETWORKPACKET_H_
#include "networkpacket.h"
#endif

#ifndef _CLIENTPACKETONSERVER_H_
#include "clientpacketonserver.h"
#endif

#ifndef _SERVERPACKETFACTORY_H_
#include "serverpacketfactory.h"
#endif

#ifndef _SERVERINFOPACKET_H_
#include "serverinfopacket.h"
#endif

#ifndef _SERVER_H_
#include "server.h"
#endif

#ifndef _SAVER_H_
#include "saver.h"
#endif

#ifndef _BOOSTSERVICE_H_
#include "boostservice.h"
#endif

#include <cassert>
#include <boost/bind.hpp>

////////////////////////////////////////////////////////////////////////////////


ServerNetwork::ServerNetwork()
{
}


////////////////////////////////////////////////////////////////////////////////


ServerNetwork::~ServerNetwork()
{
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::Start( Server* _server )
{
    m_Server = _server;

    // Setup observer
    AddEventToObserve( Observer::NETWORK_RECEIVE_COMPLETE_TCP );
    AddEventToObserve( Observer::NETWORK_RECEIVE_COMPLETE_UDP );
    AddEventToObserve( Observer::NETWORK_SEND_COMPLETE_TCP );
    AddEventToObserve( Observer::NETWORK_SEND_COMPLETE_UDP );
    AddEventToObserve( Observer::NETWORK_ACCEPTED_NEW_CONNECTION );
    AddEventToObserve( Observer::NETWORK_DISCONNECT );

    // Accept first client
    AcceptNewConnection();

    BoostService::Run();
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::AcceptNewConnection()
{
    // Create new connection
    ConnectionPtr newConnection( new Connection(Connection::SERVER) );
    m_ConnectionsByIDs.insert( std::make_pair(newConnection->GetID(), newConnection) );

    // Setup observer
    newConnection->AddObserver(this);

    // Accept new client
    newConnection->Accept();
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::HandleReceiveTCP( const Connection& _client )
{
    // Read received data into buffer
    Buffer buffer( _client.GetRecvDataTCP().Data(),
                   _client.GetRecvDataTCP().Size() );

    // Compose a client packet from it
    ClientPacketOnServer* packet = ServerPacketFactory::ComposePacket(_client.GetID(), buffer);

    // Redirect packet to server
    m_Server->Receive(packet);
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::HandleReceiveUDP( const Connection& _client )
{
    // Read received data into buffer
    Buffer buffer( _client.GetRecvDataUDP().Data(),
                   _client.GetRecvDataUDP().Size() );

    // Compose a client packet from it
    ClientPacketOnServer* packet = ServerPacketFactory::ComposePacket(_client.GetID(), buffer);

    // Redirect packet to server
    m_Server->Receive(packet);
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::SendServerState( int _clientID, const ServerStatePacket& _serverStatePacket )
{
    Saver saver;
    saver.SaveServerStatePacket(_serverStatePacket);

    SendUDP( _clientID, saver.GetBuffer() );
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::SendServerInfo( int _clientID, const ServerInfoPacket& _serverInfoPacket )
{
    Saver saver;
    saver.SaveServerInfoPacket(_serverInfoPacket);

    SendTCP( _clientID, saver.GetBuffer() );
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::SendTCP( int _clientID, const Buffer& _packet )
{
    // Compose a network packet
    NetworkPacket networkPacket( _packet.Data(), _packet.Size() );

    // Send it to client
    Connection& connection = FindConnectionByID(_clientID);

    if ( connection.IsConnected() )
        connection.SendTCP(networkPacket);
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::SendUDP( int _clientID, const Buffer& _packet )
{
    // Compose a network packet
    NetworkPacket networkPacket( _packet.Data(), _packet.Size() );

    // Send it to client
    Connection& connection = FindConnectionByID(_clientID);

    if ( connection.IsConnected() )
        connection.SendUDP(networkPacket);
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::OnEvent( Event _event, Observable* _observable )
{
    switch (_event)
    {
    case Observer::NETWORK_RECEIVE_COMPLETE_TCP:
        HandleReceiveTCP( *static_cast<Connection*>(_observable) );
        break;

    case Observer::NETWORK_RECEIVE_COMPLETE_UDP:
        HandleReceiveUDP( *static_cast<Connection*>(_observable) );
        break;

    case Observer::NETWORK_ACCEPTED_NEW_CONNECTION:
        AcceptNewConnection();
        break;

    case Observer::NETWORK_DISCONNECT:
        HandleDisconnect( *static_cast<Connection*>(_observable) );
        break;

    default:
        assert(0);
        break;
    }
}


////////////////////////////////////////////////////////////////////////////////


Connection& ServerNetwork::FindConnectionByID( int _ID )
{
    ConnectionsByIDs::iterator it = m_ConnectionsByIDs.find(_ID);

    if ( it == m_ConnectionsByIDs.end() )
        throw "Connection not found";

    return *(it->second);
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::HandleDisconnect( Connection& _client )
{
    // Close connection
    _client.Close();

    // Redirect to server
    // m_Server->Disconnect( _client.GetID() );

    // Delete connection after 1 second
    /*m_Deleter.AsyncCall( boost::bind( &ServerNetwork::DeleteConnection, this, _client.GetID() ),
                        _client.GetID(), 1000 );*/
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::DeleteConnection( int _clientID )
{
    m_ConnectionsByIDs.erase(_clientID);
}


////////////////////////////////////////////////////////////////////////////////
